package inorder_to_postorder.parser;

import inorder_to_postorder.lexer.*;
import inorder_to_postorder.symbols.Type;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Stack;


public class Parser {
    private final Lexer lex;
    private Token look;
    private final BufferedWriter out;
    private int indent = 0;
    HashMap<String, String> variables = new HashMap<>(); // 声明的变量
    Stack<String> temp_var = new Stack<>(); // 储存函数返回值的临时变量

    public Parser(Lexer l, String file_name) throws IOException {
        lex = l;
        move();
        File output_file = new File(file_name);
        if (!output_file.exists()) {
            boolean success = output_file.createNewFile();
            if (!success) {
                error("创建文件失败");
            }
        }
        out = new BufferedWriter(new FileWriter((output_file)));
    }

    void move() throws IOException {
        look = lex.scan();
    }

    void print(String s) throws IOException {
        System.out.print(s + " ");
        out.write(s + " ");
    }

    void new_line(int num) throws IOException {
        indent += num;
        System.out.print("\n");
        System.out.print(" ".repeat(Math.max(0, indent)));
        out.write("\n");
        out.write(" ".repeat(Math.max(0, indent)));
    }

    void error(String s) {
        String error_msg = "在第" + lex.line + "行：" + s;
        byte[] bytes = error_msg.getBytes(StandardCharsets.UTF_8);
        String value = new String(bytes, StandardCharsets.UTF_8);
        throw new Error(value);
    }

    void syntax_error(int t) {
        char r;
        // 大于255属于自己定义的tag，无法直接传换成字符
        if (t > 255) {
            r = Tag.translate(t);
        } else {
            r = (char) t;
        }
        error("语法错误，缺少：" + r);
    }

    // 当前字符是否是传入的字符，如果不是则抛出错误
    void match(int t) throws IOException {
        if (look.tag == t) move();
        else syntax_error(t);
    }

    // 当前字符是否是传入字符且不移动
    void match_now(int t) throws IOException {
        if (look.tag != t) syntax_error(t);
    }

    // 下一个字符是否是传入的字符
    void match_next(int t) throws IOException {
        move();
        match(t);
    }


    public void program() throws IOException {
        parse();
        out.flush();
        out.close();
    }

    void parse() throws IOException {
        switch (look.tag) {
            // int, float or string 声明变量
            case Tag.BASIC:
                manage_decls();
                parse();
                return;
            // if
            case Tag.IF:
                manage_if();
                parse();
                return;
            // 临时变量
            case Tag.TEMP:
                manage_temp();
                parse();
                return;
            // 赋值
            case Tag.ID:
                error("语法错误，不支持赋值语句");
                return;
            case Tag.NUM:
                error("语法错误，单独出现的数字");
                return;
        }
        if (manage_func_with_param()) {
            match(';');
            new_line(0);
            parse();
            return;
        }
    }

    boolean manage_func_with_param() throws IOException {
        // 一个参数的函数
        if (look.tag >= 270 && look.tag <= 279) {
            manage_one_param_func(look.toString());
        }
        // 两个参数的函数
        else if (look.tag >= 285 && look.tag <= 288) {
            manage_two_param_func(look.toString());
        }
        // 多个参数的函数
        else if (look.tag >= 292 && look.tag <= 293) {
            manage_multi_param_func(look.toString());
        } else {
            return false;
        }
        return true;
    }

    // 获取声明语句中的变量名
    String get_var_key() throws IOException {
        move();
        match_now(Tag.ID);
        String key = look.toString();
        if ((String) variables.get(key) != null || (!temp_var.empty() && temp_var.peek().equals(key))) {
            error("变量" + key + "已声明");
        }
        move();
        match('=');
        return key;
    }

    // 处理声明语句
    void manage_decls() throws IOException {
        String key = get_var_key();

        // 如果是string
        if (look == Type.STR) {
            match('"');
            variables.put(key, look.toString());
            move();
            match('"');
        } else {
            // int或者float
            variables.put(key, look.toString());
            move();
        }
        match(';');
    }

    // 处理if语句
    void manage_if() throws IOException {
        move();
        match(Tag.LP);
        // 添加缩进
        indent += 2;
        System.out.print(" ".repeat(Math.max(0, indent)));
        out.write(" ".repeat(Math.max(0, indent)));
        // if条件语句
        if_condition();
        match(Tag.RP);
        match('{');
        new_line(-2);
        print("OP_IF");
        new_line(2);
        // if语句内部
        parse();
        match('}');
        new_line(-2);
        if (look.tag == Tag.ELSE) {
            move();
            match('{');
            print("OP_ELSE");
            new_line(2);
            parse();
            new_line(-2);
            match('}');
        }
        print("OP_ENDIF");
        new_line(0);
    }


    // if条件语句的输出
    void if_condition() throws IOException {
        Stack<Token> st = new Stack<>();
        while (true) {
            switch (look.tag) {
                // 变量
                case Tag.ID:
                    String key = look.toString();

                    if (!temp_var.empty()) {
                        if (!temp_var.peek().equals(key)) {
                            new_line(0);
                        } else {
                            move();
                            continue;
                        }
                        temp_var.pop();
                    }

                    String value = variables.get(key);
                    if (value == null) {
                        error("变量" + key + "未声明");
                        return;
                    }
                    print(value);
                    move();
                    continue;
                    // 数字
                case Tag.NUM:
                case Tag.STR:
                    print(look.toString());
                    move();
                    continue;
                default:
                    break;
            }
            if (manage_func_with_param()) {
                if_condition();
            }
            // true or false
            if (look.tag == Tag.TRUE || look.tag == Tag.FALSE) {
                print(look.toString());
                move();
                break;
            }
            // 加减乘除等于
            if (look.tag >= 256 && look.tag <= 260) {
                while (!st.empty() && st.peek().tag != Tag.LP) {
                    if (look.priority() <= st.peek().priority()) {
                        print(st.pop().transfer());
                    } else {
                        break;
                    }
                }
                st.push(look);
                move();
                continue;
            }
            // 左括号直接入栈
            if (look.tag == Tag.LP) {
                st.push(look);
                move();
                continue;
            }
            // 遇到右括号，一直将栈顶弹出直到遇到左括号
            else if (look.tag == Tag.RP) {
                while (!st.empty() && st.peek().tag != Tag.LP) {
                    print(st.pop().transfer());
                }
                if (st.empty()) {
                    break;
                } else {
                    st.pop();
                }
            }
            move();
        }
    }

    // 处理一个参数的函数
    void manage_one_param_func(String func_name) throws IOException {
        move();
        match(Tag.LP);
        manage_param();
        match(Tag.RP);
        print(func_name);
    }

    // 处理两个参数的函数
    void manage_two_param_func(String func_name) throws IOException {
        move();
        match(Tag.LP);
        manage_param();
        match(',');
        manage_param();
        match(Tag.RP);
        print(func_name);
    }

    // 处理多个参数的函数
    void manage_multi_param_func(String func_name) throws IOException {
        move();
        match(Tag.LP);
        for (int i = 0; i < 2; i++) {
            match_now(Tag.NUM);
            int num = Integer.parseInt(look.toString());
            move();
            for (int j = 0; j < num; j++) {
                match(',');
                get_param();
            }
            print("" + num);
            if (i == 0) {
                match(',');
            }
        }
        match(Tag.RP);
        print(func_name);
    }

    // 判断参数是函数还是字符串、数字和变量
    void manage_param() throws IOException {
        // 函数
        if (look.tag >= 270 && look.tag <= 279) {
            manage_one_param_func(look.toString());
            return;
        } else if (look.tag >= 285 && look.tag <= 287) {
            manage_two_param_func(look.toString());
            return;
        } else if (look.tag >= 292 && look.tag <= 293) {
            manage_multi_param_func(look.toString());
            return;
        }
        get_param();
    }

    // 获取字符串、数字、变量
    void get_param() throws IOException {
        if (look.tag == Tag.ID) {
            String key = look.toString();

            if (!temp_var.empty()) {
                if (!temp_var.peek().equals(key)) {
                    new_line(0);
                } else {
                    move();
                    temp_var.pop();
                    return;
                }
                temp_var.pop();
            }

            String value = variables.get(key);
            if (value == null) {
                error("变量" + key + "未声明或不是基本类型变量");
                return;
            }
            print(value);
            move();
            return;
        } else if (look.tag == Tag.STR || look.tag == Tag.NUM) {
            print(look.toString());
            move();
            return;
        }
        error("非法参数");
    }

    // 处理临时变量
    void manage_temp() throws IOException {
        String key = get_var_key();
        if (manage_func_with_param()) {
            match(';');
            temp_var.push(key);
        } else {
            error("语法错误，只能将函数返回值赋值给temp类型变量");
        }
    }


    // 赋值语句
//    void manage_assign() throws IOException {
//        String key = look.toString();
//        if (variables.get(key) == null) {
//            error("变量" + key + "未声明");
//        }
//        move();
//        match('=');
////        int value = calc();
////        variables.put(key, "" + value);
////        match(';');
//    }

    // 计算赋值语句结果
//    int calc() {
//        int ret;
//        while (look.tag!=';'){
//            switch (look.tag){
//                // 变量
//                case Tag.ID:
//                    String key = look.toString();
//                    String value = variables.get(key);
//
//            }
//        }
//    }
}
